from rdflib import URIRef, Namespace, RDF, Literal, RDFS, BNode
from common import UCI_NS, AGENT_NS
from rdflib.Graph import Graph

import EC2
EC2_NS = Namespace("http://www.unifiedcloud.org/draft/ec2.owl#")
EC2_CLOUD_NS = Namespace('urn:ec2:cloud:')
EC2_IMAGE_NS = Namespace('urn:ec2:image:')
EC2_INSTANCE_NS = Namespace('urn:ec2:instance:')
EC2_AVAILABILITY_ZONE_NS = Namespace('urn:ec2:availabilityzone:')

class Cloud(object):
    def __init__(self, access_key):
        self.access_key = access_key
        
    @property
    def uriref(self):
        return EC2_CLOUD_NS[self.access_key]

    def serialize_to(self, graph):
        graph.add((self.uriref, RDF.type, EC2_NS['EC2Cloud']))
        graph.add((self.uriref, RDFS.label, Literal('EC2 with key %s' % self.access_key)))

class Instance(object):
    def __init__(self, cloud, data):
        self.cloud = cloud
        assert(data[0] == 'INSTANCE')
        
        self.id = data[1]
        self.image_id = data[2]
        self.dns_name = data[3]
        self.private_dns_name = data[4]
        self.instance_state = data[5]
        self.instance_type = data[9]
        self.availability_zone = data[11]


    @property
    def uriref(self):
        return EC2_INSTANCE_NS[self.id]
        
    def serialize_to(self, graph):
         
#        if self.instance_type == 'm1.small':
#            itype = EC2_NS['SmallInstance']
#        elif self.instance_type == 'm1.large':
#            itype = EC2_NS['LargeInstance']
#        elif self.instance_type == 'm1.xlarge':
#            itype = EC2_NS['ExtraLargeInstance']
#        elif self.instance_type == 'c1.medium':
#            itype = EC2_NS['HighCpuMediumInstance']
#        elif self.instance_type == 'c1.xlarge':
#            itype = EC2_NS['HighCpuExtraLargeInstance']
#        else:
#            itype = EC2_NS['EC2Instance']
        itype = EC2_NS['EC2Instance']
            
        graph.add((self.uriref, RDF.type, itype))
        if self.dns_name:
            graph.add((self.uriref, RDFS.label, Literal('%s (%s)' % (self.id, self.dns_name))))
        else:
            graph.add((self.uriref, RDFS.label, Literal(self.id)))
        graph.add((self.uriref, UCI_NS['belongs_to_cloud'], self.cloud.uriref))
        graph.add((self.uriref, UCI_NS['machine_state'], UCI_NS['machine_%s' % self.instance_state]))        
        graph.add((self.uriref, UCI_NS['site'], EC2_AVAILABILITY_ZONE_NS[self.availability_zone]))
        
        graph.add((self.uriref, UCI_NS['dns_name'], Literal(self.dns_name)))
        graph.add((self.uriref, EC2_NS['private_dns_name'], Literal(self.private_dns_name)))
        graph.add((self.uriref, EC2_NS['instance_type'], Literal(self.instance_type)))
        graph.add((self.uriref, UCI_NS['package'], EC2_IMAGE_NS[self.image_id]))

class VmImage(object):
    def __init__(self, cloud, data):
        self.cloud = cloud
        assert(data[0] == 'IMAGE')
        self.id = data[1]
        self.location = data[2]
        self.owner = data[3]
        
    @property
    def uriref(self):
        return EC2_IMAGE_NS[self.id]
        
    def __unicode__(self):
        return unicode(self.uriref)
        
    def serialize_to(self, graph):
        graph.add((self.uriref, RDF.type, UCI_NS['VirtualMachinePackage']))
        graph.add((self.uriref, RDFS.label, Literal(self.id)))
        graph.add((self.uriref, UCI_NS['belongs_to_cloud'], self.cloud.uriref))
    
class Plugin(object):
    """EC2 plugin"""
    def __init__(self, config_file, section):
        self.access_key = config_file.get(section, 'access_key')
        self.secret_key = config_file.get(section, 'secret_key')

        self.cloud = Cloud(self.access_key)
        self.conn = EC2.AWSAuthConnection(self.access_key, self.secret_key)
        
        
        self.images = {}
        self.instances = {}
        self.uriref_map = {}
        
    def initialize(self):
        self._load_images()
        self._load_instances()
        self._build_uriref_map()
        
    def is_uri_from_this_plugin(self, uriref):
        return uriref in self.uriref_map
        
    def get_state(self, request, graph):
        g = Graph()

        self.cloud.serialize_to(g)
        
        for i in self.instances.itervalues():
            i.serialize_to(g)
            
        for i in self.images.itervalues():
            i.serialize_to(g)
    
        return g
    
    def start_instance(self, request, graph):
        raise NotImplementedError
    
    def stop_instance(self, request, graph):
        raise NotImplementedError
    
    def provision(self, request, graph):
        for image_uri in graph.objects(request, AGENT_NS['package']):
            print "Provisioning %s" % image_uri
            image = self.uriref_map[image_uri]
            assert isinstance(image, VmImage)
                        
            resp = self.conn.run_instances(image.id)
            assert(not resp.is_error)
        return Graph()
    
    def destroy(self, request, graph):
        machine_uri = graph.value(subject=request, predicate=AGENT_NS['virtual_machine'])
        instance = self.uriref_map[machine_uri]
        assert(isinstance(instance, Instance))

        print "Destroying %s (%s)" % (instance.id, instance.dns_name)
        resp = self.conn.terminate_instances([instance.id])

        if resp.is_error:
            print "Error: %s" % resp.structure
            raise Exception("Got error: %r" % resp.structure)
        
        return Graph()

    def _load_images(self):
        images = [VmImage(self.cloud, data) for data in self.conn.describe_images().structure if data[2].startswith('ec2-public-images')]
        self.images = dict((x.id, x) for x in images)

    def _load_instances(self):
        instances = [Instance(self.cloud, data) for data in self.conn.describe_instances().structure if data[0]=='INSTANCE']
        self.instances = dict((x.id, x) for x in instances)
        
    def _build_uriref_map(self):
        self.uriref_map[self.cloud.uriref] = self.cloud
        
        for i in self.images.itervalues():
            self.uriref_map[i.uriref] = i
            
        for i in self.instances.itervalues():
            self.uriref_map[i.uriref] = i